﻿using System;
using System.Collections.Generic;
using ZyGame.Execute;
using System.IO;
using System.Collections;
using ZyGame.Datable;
using UnityEngine;
using Object = UnityEngine.Object;

namespace ZyGame.Resource
{
    sealed class BundleRefrence : IDatable
    {
        public string name { get; }
        private int refCount { get; set; }
        private AssetBundle assetBundle;
        private List<Object> objects = new List<Object>();
        private List<int> instance_id_List = new List<int>();

        public BundleRefrence(string name)
        {
            this.name = name;
        }

        public BundleRefrence(AssetBundle bundle) : this(bundle.name)
        {
            assetBundle = bundle;
        }


        public bool TryUnload()
        {
            //Client.Console.WriteLine($"{name} Ref Count:{refCount}");
            if (refCount > 0)
            {
                return false;
            }

            return true;
        }

        public bool HasHaveAsset(string url)
        {
#if UNITY_EDITOR
            if (AppConfig.HasUseAssetsHotfix() is false)
            {
                if (url.StartsWith("Assets"))
                {
                    return UnityEditor.AssetDatabase.LoadAssetAtPath<Object>(url) is not null;
                }

                if (url.StartsWith("Resources"))
                {
                    string path = url.Substring(url.IndexOf("/") + 1);
                    return Resources.Load(Path.GetFileNameWithoutExtension(path)) is not null;
                }
            }
#endif
            if (assetBundle is null)
            {
                return false;
            }

            Client.Console.WriteLine(Path.GetFileNameWithoutExtension(url));
            return assetBundle.Contains(Path.GetFileNameWithoutExtension(url));
        }

        public bool HasHaveAsset(Object target)
        {
            return instance_id_List.Contains(target.GetInstanceID());
        }

        public T GetObject<T>(string url, GameObject bindObject = null) where T : Object
        {
            string name = Path.GetFileNameWithoutExtension(url);
            Object result = objects.Find(x => x.name == name && x.GetType() == typeof(T));
            if (result is not null)
            {
                return Instantiate<T>(result);
            }

            if (url.StartsWith("Resources"))
            {
                string path = Path.GetFileNameWithoutExtension(url.Substring(url.IndexOf("/") + 1));
                result = Resources.Load<T>(path);
                if (result is not null)
                {
                    objects.Add(result);
                }

                return Instantiate<T>(result);
            }

#if UNITY_EDITOR
            if (AppConfig.HasUseAssetsHotfix() is false)
            {
                result = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(url);
                if (result is not null)
                {
                    objects.Add(result);
                }
                else
                {
                    Client.Console.WriteLine(new Exception("加载资源失败，请检查资源路径是否正确：" + url));
                    return default;
                }

                return Instantiate<T>(result);
            }
#endif
            if (assetBundle is null || assetBundle.Contains(url) is false)
            {
                Client.Console.WriteError($"[LOAD ASSET FAIL] {assetBundle is null} {assetBundle.Contains(url)}");
                return default;
            }

            result = assetBundle.LoadAsset<T>(url);
            if (result is not null)
            {
                objects.Add(result);
            }

            return Instantiate<T>(result);
        }

        public IEnumerator GetObjectAsync<T>(string url, Runnable<T> runnable, GameObject bindObject = null) where T : Object
        {
            string name = Path.GetFileNameWithoutExtension(url);
            Object result = objects.Find(x => x.name == name);
            if (result is not null)
            {
                runnable.SetResult(Instantiate<T>(result));
                yield break;
            }

            if (url.StartsWith("Resources"))
            {
                string path = url.Substring(url.IndexOf("/") + 1);
                ResourceRequest request = Resources.LoadAsync<T>(Path.GetFileNameWithoutExtension(path));
                yield return request;
                if (request.isDone is false || request.asset is null)
                {
                    runnable.SetResult(new Exception("加载资源失败，请检查资源路径是否正确：" + url));
                    yield break;
                }

                objects.Add(request.asset);
                runnable.SetResult(Instantiate<T>(request.asset));
                yield break;
            }
#if UNITY_EDITOR
            if (AppConfig.HasUseAssetsHotfix() is false)
            {
                result = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(url);
                if (result is null)
                {
                    runnable.SetResult(new Exception("加载资源失败，请检查资源路径是否正确：" + url));
                    yield break;
                }

                objects.Add(result);
                runnable.SetResult(Instantiate<T>(result));
                yield break;
            }
#endif
            if (assetBundle is null)
            {
                runnable.SetResult(new Exception("加载资源失败，请检查资源路径是否正确：" + url));
                yield break;
            }

            AssetBundleRequest bundleRequest = assetBundle.LoadAssetAsync<T>(Path.GetFileNameWithoutExtension(url));
            yield return bundleRequest;
            if (bundleRequest.isDone is false || bundleRequest.asset is null)
            {
                runnable.SetResult(new Exception("加载资源失败，请检查资源路径是否正确：" + url));
                yield break;
            }

            objects.Add(bundleRequest.asset);
            runnable.SetResult(Instantiate<T>(bundleRequest.asset));
        }

        public T Instantiate<T>(Object target, GameObject bind = null) where T : Object
        {
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }

            refCount++;
            T result = target as T;
            if (typeof(GameObject) == typeof(T))
            {
                result = (T)Object.Instantiate(target);
                bind = result as GameObject;
            }

            if (bind != null)
            {
                Client.Tools.DestroyCallback(bind, Client.Resource.Release, result);
            }

            instance_id_List.Add(result.GetInstanceID());
            return result;
        }

        public void Release(Object target)
        {
            refCount--;
            instance_id_List.Remove(target.GetInstanceID());
        }

        public void Dispose()
        {
            objects.Clear();
            refCount = 0;
            if (assetBundle is not null)
            {
                assetBundle.Unload(true);
            }

            Client.Console.WriteLine("Unload AssetBundle:" + name);
        }
    }
}